package server;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author yuanxp
 * @Date 2021/4/19 10:43
 */
public class Bootstrap {
    private Map<String, HttpServlet> servletMap = new HashMap<String, HttpServlet>();


    private int port = 8080;

    private Mapper mapper;


    public void start() throws Exception {

//        loadServlet();


        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );

        // 接收/处理请求
        ServerSocket serverSocket = new ServerSocket(port);
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, mapper);
            threadPoolExecutor.execute(requestProcessor);
        }
    }

    /**
     * 加载解析web.xml，初始化Servlet
     */
    private void loadServlet(WebappClassLoader webAppClassLoader, Mapper.MappedContext mappedContext,File webConfigPatch) throws Exception {
        InputStream resourceAsStream = new FileInputStream(webConfigPatch);
        SAXReader saxReader = new SAXReader();

        Document document = saxReader.read(resourceAsStream);
        Element rootElement = document.getRootElement();

        List<Element> selectNodes = rootElement.selectNodes("//servlet");
        for (int i = 0; i < selectNodes.size(); i++) {
            Element element = selectNodes.get(i);
            // <servlet-name>lagou</servlet-name>
            Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
            String servletName = servletnameElement.getStringValue();
            // <servlet-class>server.LagouServlet</servlet-class>
            Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
            String servletClass = servletclassElement.getStringValue();
            // 将com.A 替换成com.
            servletClass = servletClass.replaceAll("\\.","/");


            // 根据servlet-name的值找到url-pattern
            Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
            // /lagou
            String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
            //封装Wrapper
            Mapper.MappedWrapper mappedWrapper = new Mapper.MappedWrapper(urlPattern, new Wrapper(urlPattern, (HttpServlet) webAppClassLoader.findClass(webConfigPatch.getParent() + "/" + servletClass).newInstance()));
            mappedContext.addWrapper(mappedWrapper);
        }
    }


    public static void main(String[] args) throws Exception {
        Bootstrap bootstrap = new Bootstrap();
        // 解析server.xml文件
        bootstrap.init();
        bootstrap.start();
    }

    private void init() throws Exception {
        // 解析 server.xml
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();

        Document document = saxReader.read(resourceAsStream);
        Element rootElement = document.getRootElement();

        Element connectorElement = (Element) rootElement.selectSingleNode("/Server/Service/Connector");
        String port = connectorElement.attribute("port").getStringValue();
        this.port = Integer.valueOf(port);

        //初始化Host
        Element hostElement = (Element) rootElement.selectSingleNode("/Server/Service/Engine/Host");
        String appBase = hostElement.attribute("appBase").getStringValue();
        String name = hostElement.attribute("name").getStringValue();
        mapper = new Mapper(new Mapper.MappedHost(name,new Host(name,appBase)));
        Mapper.MappedHost mappedHost = mapper.getDefaultHost();

        //初始化Context
        List<Element> contextElements = rootElement.selectNodes("/Server/Service/Engine/Host/Context");
        if(connectorElement != null){
            contextElements.stream().forEach(con->{
                String docBase = con.attribute("docBase").getStringValue();
                String path = con.attribute("path").getStringValue();
                mappedHost.addContext(new Mapper.MappedContext(docBase,new Context(docBase,path)));
            });
        }



        File appBaseFile = new File(appBase);
        if (!appBaseFile.exists()) {
            throw new RuntimeException("appBase 路径不存在");
        }
        // 解析的是项目工程
        File[] webApps = appBaseFile.listFiles();
        for (File webApp : webApps) {
            // 初始化 自定义ClassLoader
            WebappClassLoader webAppClassLoader = new WebappClassLoader();

            String path = "/" + webApp.getName();
            Mapper.MappedContext mappedContext = new Mapper.MappedContext(path, new Context(webApp.getAbsolutePath(), path));
            mappedHost.addContext(mappedContext);
            loaderDir(webAppClassLoader,mappedContext,webApp);
        }

    }

    private void loaderDir(WebappClassLoader webAppClassLoader,Mapper.MappedContext mappedContext, File webApp) throws Exception{
        if (webApp == null || webApp.listFiles() == null) {
            return;
        }
        for (File file : webApp.listFiles()) {
            if (file.isDirectory()) {
                loaderDir(webAppClassLoader, mappedContext, file);
            } else if (file.isFile() && file.getName().equals("web.xml")) {
                // 加载解析相关的配置，web.xml
                loadServlet(webAppClassLoader,mappedContext,file);
            }
        }
    }
}
